package com.github.ghsea.cc.service.analysis;

import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import javax.annotation.Resource;

import org.apache.storm.shade.com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.github.ghsea.cc.consts.DurationTypeEnum;
import com.github.ghsea.cc.module.AccessTimes;
import com.github.ghsea.cc.module.Rule;
import com.google.common.base.Optional;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

/**
 * 1分钟内的访问结果缓存
 * 
 * @author GuHai 2017-7-29上午11:41:28
 */
@Component
public class Statistics {

	private Logger log = LoggerFactory.getLogger(Statistics.class);

	/**
	 * cache key = IP cache <br>
	 * value = map <br>
	 * ------key=url
	 * pattern,例如url=/test/index.jsp,如果这个url与设置防CC的规则中指定的url规则(例如/test/.*)一致<br>
	 * ，将以防CC的url规则作为key <br>
	 * <br>
	 * 缓存将在访问后60s过期。在过期的时候将通知CacheRemoveListener，以计算该IP是否触发了防CC规则
	 */
	private Cache<String, Map<String, AccessTimes>> cacheFor1M = CacheBuilder.newBuilder().maximumSize(100000)
			.expireAfterWrite(59, TimeUnit.SECONDS).build();

	/**
	 * 1小时的缓存
	 */
	private Cache<String, Map<String, AccessTimes>> cacheFor1H = CacheBuilder.newBuilder().maximumSize(100000)
			.expireAfterWrite(3599, TimeUnit.SECONDS).build();

	@Resource
	private RuleEngine ruleEngine;

	/**
	 * 如果访问请求符合防CC规则，则增加一次访问请求次数
	 * 
	 * @param ip
	 * @param requestUrl
	 */
	public void incrTimesAndCheckCC(String accessIp, String reqeustHost, String requestUrl, Date accessTime) {
		Optional<Rule> ruleUrlOpt = ruleEngine.matchRuleUrl(reqeustHost, requestUrl);
		if (ruleUrlOpt.isPresent()) {
			Rule rule = ruleUrlOpt.get();
			doIncrTimesAndCheckCC(rule, accessIp, reqeustHost, accessTime, cacheFor1M);
			doIncrTimesAndCheckCC(rule, accessIp, reqeustHost, accessTime, cacheFor1H);
		}
	}

	private void doIncrTimesAndCheckCC(Rule rule, String ip, String requestHost, Date accessTime,
			Cache<String, Map<String, AccessTimes>> cache) {
		String urlPattern = rule.getUrlPattern();
		Map<String, AccessTimes> urlPattern2Times = cache.getIfPresent(ip);
		AccessTimes accessTimes = null;

		String url = requestHost + urlPattern;
		if (urlPattern2Times != null) {
			accessTimes = urlPattern2Times.get(url);
		}
		if (urlPattern2Times == null || accessTimes == null) {
			accessTimes = new AccessTimes();
			if (cache == cacheFor1M) {
				accessTimes.setDurationType(DurationTypeEnum.Minute.getType());
			} else if (cache == cacheFor1H) {
				accessTimes.setDurationType(DurationTypeEnum.Hour.getType());
			}
			accessTimes.setIp(ip);
			accessTimes.setTimes(new AtomicInteger(1));
			accessTimes.setFirstAccessTime(accessTime);
			Map<String, AccessTimes> value = Maps.newHashMap();
			value.put(url, accessTimes);
			cache.asMap().putIfAbsent(ip, value);
		} else {
			accessTimes.setThisAccessTime(accessTime);
			accessTimes.getTimes().incrementAndGet();
			boolean intercepted = ruleEngine.checkAndLog(rule, accessTimes);
			// if (intercepted) {
			// Map<String, AccessTimes> urlPat2Times = cache.getIfPresent(ip);
			// urlPat2Times.remove(rule.getUrlPattern());
			// }
		}
	}
	// private class CacheRemoveListener implements RemovalListener<String,
	// Map<String, AccessTimes>> {
	// public void onRemoval(RemovalNotification<String, Map<String,
	// AccessTimes>> notification) {
	// String ip = notification.getKey();
	// Map<String, AccessTimes> cache = notification.getValue();
	// Set<Entry<String, AccessTimes>> entrySet = cache.entrySet();
	// for (Entry<String, AccessTimes> entry : entrySet) {
	// String urlPattern = entry.getKey();
	// AccessTimes accessTimes = entry.getValue();
	// accessTimes.setIp(ip);
	// accessTimes.setDurationType(DurationTypeEnum.Minute.getType());
	// ruleEngine.checkAndLog(urlPattern, accessTimes);
	// }
	// }
	// }

}
